﻿// HSS.Interlink.UI.UploadControl.cs
// ----------------------------------------------------------------------------
// Licensed under the MIT license (http://www.opensource.org/licenses/mit-license.html)
// ----------------------------------------------------------------------------
// HighSpeed-Solutions, LLC
// Copyright (c) 2001-2012
// ----------------------------------------------------------------------------
// File:       UploadControl.cs
// Author:     HSS\gbanta
// Created:    01/17/2012
// Modified:   08/18/2012
// ----------------------------------------------------------------------------
namespace HSS.Interlink.UI
{
	#region Using Directives
	using System;
	using System.Collections.Generic;
	using System.ComponentModel;
	using System.IO;
	using System.Windows;
	using System.Windows.Controls;
	using System.Windows.Data;
	using System.Windows.Input;
	using System.Collections.ObjectModel;

	#endregion

	#region UploadControl
	/// <summary>
	/// A user control for uploading one or more files.
	/// </summary>
	[TemplateVisualState(GroupName = "UploadingStates", Name = "NotStarted")]
	[TemplateVisualState(GroupName = "UploadingStates", Name = "Pending")]
	[TemplateVisualState(GroupName = "UploadingStates", Name = "PendingSingleFile")]
	[TemplateVisualState(GroupName = "UploadingStates", Name = "InProgress")]
	[TemplateVisualState(GroupName = "UploadingStates", Name = "Canceled")]
	[TemplateVisualState(GroupName = "UploadingStates", Name = "Completed")]
	[TemplateVisualState(GroupName = "UploadingStates", Name = "PartialComplete")]
	[TemplateVisualState(GroupName = "UploadingStates", Name = "CompletedDone")]
	[TemplateVisualState(GroupName = "UploadingStates", Name = "PartialCompleteDone")]
	[TemplateVisualState(GroupName = "UploadingStates", Name = "CanceledDone")]
	[TemplatePart(Name = "PART_ContentRoot", Type = typeof(Grid))]
	[TemplatePart(Name = "PART_FileListBox", Type = typeof(ListBox))]
	[TemplatePart(Name = "PART_FileColumnHeader", Type = typeof(FrameworkElement))]
	public class UploadControl : Control, HSS.Interlink.UI.IUploadConfiguration
	{
		#region Constants
		/// <summary>
		/// The default Client Type (Default: 'HSS.Interlink.UI.Client')
		/// </summary>
		public const string DefaultClientType = "HSS.Interlink.UploadClient";
		/// <summary>
		/// The default WebConfig ApplicationSetting Key (Default: 'UploadHandler') that contains the Handler for the file upload.
		/// </summary>
		public const string DefaultClientConfig = "UploadHandler";
		/// <summary>
		/// The default ServiceUri (Default: '/FileUpload.ashx') that matches the Handler Path in the Web.Config
		/// </summary>
		public const string DefaultServiceUri = "/FileUpload.ashx";
		#endregion

		#region Events

		#region GetMetadata
		/// <summary>
		/// An event that is raised just before a file is uploaded,
		/// allowing you to provide metadata to be sent along with
		/// the file, such as a custom file id or an associated
		/// record id or any string that you want to pass.
		/// </summary>
		public event EventHandler<GetMetadataEventArgs> GetMetadata;
		private void OnGetMetadata(GetMetadataEventArgs e)
		{
			var handler = this.GetMetadata;
			if (null != handler)
				handler(this, e);
		}
		#endregion

		#region ViewFileDetails
		/// <summary>
		/// Occurs when the user selects the View Details context menu of a particular file
		/// requesting to show a dialog or child window displaying the details of that file.
		/// </summary>
		public event EventHandler<ViewFileDetailsEventArgs> ViewFileDetails;
		private void OnViewFileDetails(ViewFileDetailsEventArgs e)
		{
			var handler = ViewFileDetails;
			if (null != handler)
			{
				handler(this, e);
			}
			else
			{
				var dlg = new UploadFileDetailDialog();
				dlg.File = e.File;
				dlg.Show();
			}
		}
		#endregion

		#region UploadCompleted
		/// <summary>
		/// Occurs when the upload completes.
		/// </summary>
		public event EventHandler UploadCompleted;
		private void OnUploadCompleted(EventArgs e)
		{
			var handler = UploadCompleted;
			if (null != handler)
				handler(this, e);
		}
		#endregion

		#endregion

		#region Fields

		UploadControlViewModel viewModel;
		FrameworkElement PART_FileColumnHeader;
		Grid PART_ContentRoot;
		ListBox PART_FileListBox;
		#endregion

		#region Constuctors
		/// <summary>
		/// Constuctor
		/// </summary>
		public UploadControl()
		{
			this.DefaultStyleKey = typeof(UploadControl);
			this.viewModel = new UploadControlViewModel(this, this.Dispatcher);
			this.viewModel.UploadCompleted += viewModel_UploadCompleted;
			this.viewModel.ViewFileDetails += viewModel_ViewFileDetails;
			this.viewModel.GetMetadata += viewModel_GetMetadata;
			this.viewModel.FileAdded += viewModel_FileAdded;
			this.DataContext = this.viewModel;
		}
		#endregion

		#region Properties

		#region IUploadConfiguration

		#region AllowFileOverwrite
		/// <summary>
		/// Gets or sets whether or not to overwrite existing files.
		/// </summary>
		[DefaultValue(true)]
		[Category("Interlink")]
		[Description("Gets or sets whether or not to overwrite existing files.")]
		public bool AllowFileOverwrite
		{
			get { return (bool)GetValue(AllowFileOverwriteProperty); }
			set { SetValue(AllowFileOverwriteProperty, value); }
		}
		/// <summary>
		/// Using a DependencyProperty as the backing store for AllowFileOverwrite.  This enables animation, styling, binding, etc...
		/// </summary>
		public static readonly DependencyProperty AllowFileOverwriteProperty =
			DependencyProperty.Register("AllowFileOverwrite", typeof(bool), typeof(UploadControl), new PropertyMetadata(true));
		#endregion

		#region AllowMultiSelect
		/// <summary>
		/// Gets or sets whether or not the user can select multiple files (Default: True).
		/// </summary>
		[DefaultValue(true)]
		[Category("Interlink")]
		[Description("Gets or sets whether or not the user can select multiple files (Default: True).")]
		public bool AllowMultiSelect
		{
			get { return (bool)GetValue(AllowMultiSelectProperty); }
			set { SetValue(AllowMultiSelectProperty, value); }
		}
		/// <summary>
		/// Using a DependencyProperty as the backing store for AllowMultiSelect.  This enables animation, styling, binding, etc...
		/// </summary>
		[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Multi")]
		public static readonly DependencyProperty AllowMultiSelectProperty =
			DependencyProperty.Register("AllowMultiSelect", typeof(bool), typeof(UploadControl), new PropertyMetadata(true));
		#endregion

		#region AllowNewUpload
		/// <summary>
		/// Gets or sets whether or not once the first upload process completes, if the User can initiate a new Upload.
		/// </summary>
		[DefaultValue(true)]
		[Category("Interlink")]
		[Description("Gets or sets whether or not once the first upload process completes, if the User can initiate a new Upload.")]
		public bool AllowNewUpload
		{
			get { return (bool)GetValue(AllowNewUploadProperty); }
			set { SetValue(AllowNewUploadProperty, value); }
		}
		/// <summary>
		/// Using a DependencyProperty as the backing store for AllowNewUpload.  This enables animation, styling, binding, etc...
		/// </summary>
		public static readonly DependencyProperty AllowNewUploadProperty =
			DependencyProperty.Register("AllowNewUpload", typeof(bool), typeof(UploadControl), new PropertyMetadata(true));
		#endregion

		#region AutoUpload
		/// <summary>
		/// Gets or sets whether or not to begin uploading immediately after file selection (Default: false).
		/// </summary>
		/// <remarks><b>Default Value is </b> <c>false</c>.</remarks>
		[DefaultValue(true)]
		[Category("Interlink")]
		[Description("Gets or sets whether or not to begin uploading immediately after file selection (Default: false).")]
		public bool AutoUpload
		{
			get { return (bool)GetValue(AutoUploadProperty); }
			set { SetValue(AutoUploadProperty, value); }
		}
		/// <summary>
		/// Using a DependencyProperty as the backing store for AutoUpload.  This enables animation, styling, binding, etc...
		/// </summary>
		public static readonly DependencyProperty AutoUploadProperty =
			DependencyProperty.Register("AutoUpload", typeof(bool), typeof(UploadControl), new PropertyMetadata(false));
		#endregion

		#region ClientConfig
		/// <summary>
		/// Gets or sets the configuration string to provide to the configurated <see cref="IUploadClient"/> client during intialization.
		/// </summary>
		[DefaultValue(UploadControl.DefaultClientConfig)]
		[Category("Interlink")]
		[Description("The configuration string to provide to the configurated IUploadClient client during intialization.")]
		public string ClientConfig
		{
			get { return (string)GetValue(ClientConfigProperty); }
			set { SetValue(ClientConfigProperty, value); }
		}
		/// <summary>
		/// Using a DependencyProperty as the backing store for ClientConfig.  This enables animation, styling, binding, etc...
		/// </summary>
		public static readonly DependencyProperty ClientConfigProperty =
			DependencyProperty.Register("ClientConfig", typeof(string), typeof(UploadControl), new PropertyMetadata(UploadControl.DefaultClientConfig));
		#endregion

		#region ClientType
		/// <summary>
		/// Gets or sets a string defining the Type of the <see cref="IUploadClient"/> that will process the upload for a single file.
		/// </summary>
		[DefaultValue(UploadControl.DefaultClientType)]
		[Category("Interlink")]
		[Description("A string defining the Type of the IUploadClient that will process the upload for a single file.")]
		public string ClientType
		{
			get { return (string)GetValue(ClientTypeProperty); }
			set { SetValue(ClientTypeProperty, value); }
		}
		/// <summary>
		/// Using a DependencyProperty as the backing store for ClientType.  This enables animation, styling, binding, etc...
		/// </summary>
		public static readonly DependencyProperty ClientTypeProperty =
			DependencyProperty.Register("ClientType", typeof(string), typeof(UploadControl), new PropertyMetadata(UploadControl.DefaultClientType));
		#endregion

		#region Comment
		/// <summary>
		/// Gets or sets the comment to display in the header area.
		/// </summary>
		[DefaultValue("Select one or more files to be uploaded.")]
		[Category("Interlink")]
		[Description("The comment to display in the header area.")]
		public string Comment
		{
			get { return (string)GetValue(CommentProperty); }
			set
			{
				if (string.IsNullOrEmpty(value))
					value = "The following files will be uploaded.";
				SetValue(CommentProperty, value);
			}
		}
		/// <summary>
		/// Using a DependencyProperty as the backing store for Comment.  This enables animation, styling, binding, etc...
		/// </summary>
		public static readonly DependencyProperty CommentProperty =
			DependencyProperty.Register("Comment", typeof(string), typeof(UploadControl), new PropertyMetadata("Select one or more files to be uploaded."));
		#endregion

		#region ExcludeFileExtension
		/// <summary>
		/// Gets or sets the pipe delimited list of file extensions that are prohibited. Example: .exe|.mdb
		/// </summary>
		[DefaultValue(".exe|.mdb|.mde|.accdb|.accde")]
		[Category("Interlink")]
		[Description("The pipe delimited list of file extensions that are prohibited. Example: .exe|.mdb")]
		public string ExcludeFileExtension
		{
			get { return (string)GetValue(ExcludeFileExtensionProperty); }
			set { SetValue(ExcludeFileExtensionProperty, value); }
		}
		/// <summary>
		/// Using a DependencyProperty as the backing store for ExcludeFileExtension.  This enables animation, styling, binding, etc...
		/// </summary>
		public static readonly DependencyProperty ExcludeFileExtensionProperty =
			DependencyProperty.Register("ExcludeFileExtension", typeof(string), typeof(UploadControl), new PropertyMetadata(".exe|.mdb|.mde|.accdb|.accde"));
		#endregion

		#region Filters
		/// <summary>
		/// Gets or sets a pipe delimeted string of allowed file extensions that can be uploaded.
		/// </summary>
		/// <remarks>
		/// <para>
		/// Should use the same format as the <see cref="OpenFileDialog"/>.
		/// </para>
		/// </remarks>
		[DefaultValue("All Files (*.*)|*.*")]
		[Category("Interlink")]
		[Description("A pipe delimeted string of allowed file extensions that can be uploaded.")]
		public string Filters
		{
			get { return (string)GetValue(FiltersProperty); }
			set { SetValue(FiltersProperty, value); }
		}
		/// <summary>
		/// Using a DependencyProperty as the backing store for Filters.  This enables animation, styling, binding, etc...
		/// </summary>
		public static readonly DependencyProperty FiltersProperty =
			DependencyProperty.Register("Filters", typeof(string), typeof(UploadControl), new PropertyMetadata("All Files (*.*)|*.*"));
		#endregion

		#region MaximumConcurrency
		/// <summary>
		/// Gets or sets the maximum number of concurrent files that can be uploaded at a time. Default: 2
		/// </summary>
		[DefaultValue(2)]
		[Category("Interlink")]
		[Description("The maximum number of concurrent files that can be uploaded at a time. Default: 2")]
		public int MaximumConcurrency
		{
			get { return (int)GetValue(MaximumConcurrencyProperty); }
			set { SetValue(MaximumConcurrencyProperty, value); }
		}
		/// <summary>
		/// Using a DependencyProperty as the backing store for MaximumConcurrency.  This enables animation, styling, binding, etc...
		/// </summary>
		public static readonly DependencyProperty MaximumConcurrencyProperty =
			DependencyProperty.Register("MaximumConcurrency", typeof(int), typeof(UploadControl), new PropertyMetadata(2));
		#endregion

		#region MaximumFileSize
		/// <summary>
		/// Gets or sets the maximum size, in bytes, of any one file that can be uploaded. Default: Int32.MaxValue.
		/// </summary>
		[DefaultValue(int.MaxValue)]
		[Category("Interlink")]
		[Description("The maximum size, in bytes, of any one file that can be uploaded. Default: Int32.MaxValue.")]
		public int MaximumFileSize
		{
			get { return (int)GetValue(MaximumFileSizeProperty); }
			set { SetValue(MaximumFileSizeProperty, value); }
		}
		/// <summary>
		/// Using a DependencyProperty as the backing store for MaximumFileSize.  This enables animation, styling, binding, etc...
		/// </summary>
		public static readonly DependencyProperty MaximumFileSizeProperty =
			DependencyProperty.Register("MaximumFileSize", typeof(int), typeof(UploadControl), new PropertyMetadata(int.MaxValue));
		#endregion

		#region NoFileText
		/// <summary>
		/// Gets or sets the text to be displayed when there are no files selected for upload.
		/// </summary>
		[Category("Interlink")]
		[Description("The text to be displayed when there are no files selected for upload.")]
		public string NoFilesText
		{
			get { return (string)GetValue(NoFilesTextProperty); }
			set
			{
				if (string.IsNullOrEmpty(value))
					value = HSS.Interlink.Strings.NoFilesText;
				SetValue(NoFilesTextProperty, value);
			}
		}
		/// <summary>
		/// Using a DependencyProperty as the backing store for NoFileText.  This enables animation, styling, binding, etc...
		/// </summary>
		public static readonly DependencyProperty NoFilesTextProperty =
			DependencyProperty.Register("NoFilesText", typeof(string), typeof(UploadControl), new PropertyMetadata(HSS.Interlink.Strings.NoFilesText));
		#endregion

		#region ServiceUri
		/// <summary>
		/// Gets or sets the uri (absolute or relative) to the upload service to receive the file(s). For OOB must be the absolute Uri. Default: '/FileUpload.ashx'.
		/// </summary>
		[DefaultValue(UploadControl.DefaultServiceUri)]
		[Category("Interlink")]
		[Description("The uri (absolute or relative) to the upload service to receive the file(s). For OOB must be the absolute Uri. Default: '/FileUpload.ashx'.")]
		public string ServiceUri
		{
			get { return (string)GetValue(ServiceUriProperty); }
			set { SetValue(ServiceUriProperty, value); }
		}
		/// <summary>
		/// Using a DependencyProperty as the backing store for ServiceUri.  This enables animation, styling, binding, etc...
		/// </summary>
		public static readonly DependencyProperty ServiceUriProperty =
			DependencyProperty.Register("ServiceUri", typeof(string), typeof(UploadControl), new PropertyMetadata(UploadControl.DefaultServiceUri));
		#endregion

		#region Title
		/// <summary>
		/// Gets or sets the title to display in the header area.
		/// </summary>
		[DefaultValue("Upload Files")]
		[Category("Interlink")]
		[Description("The title to display in the header area.")]
		public string Title
		{
			get { return (string)GetValue(TitleProperty); }
			set
			{
				if (string.IsNullOrEmpty(value))
					value = "Upload Folder";
				SetValue(TitleProperty, value);
			}
		}
		/// <summary>
		/// Using a DependencyProperty as the backing store for Title.  This enables animation, styling, binding, etc...
		/// </summary>
		public static readonly DependencyProperty TitleProperty =
			DependencyProperty.Register("Title", typeof(string), typeof(UploadControl), new PropertyMetadata("Upload Files"));
		#endregion

		#region UploadFileDataTemplate
		/// <summary>
		/// Gets or sets the Uri of a <see cref="DataTemplate"/> resource to load, that supports the Type defined in the <see cref="UploadFileType"/> property.
		/// </summary>
		[DefaultValue("/HSS.Interlink.UI;component/uploadfile.xaml")]
		[Category("Interlink")]
		[Description("The Uri of a DataTemplate resource to load, that supports the Type defined in the UploadFileType property.")]
		public string UploadFileDataTemplate
		{
			get { return (string)GetValue(UploadFileDataTemplateProperty); }
			set { SetValue(UploadFileDataTemplateProperty, value); }
		}
		/// <summary>
		/// Using a DependencyProperty as the backing store for UploadFileDataTemplate.  This enables animation, styling, binding, etc...
		/// </summary>
		public static readonly DependencyProperty UploadFileDataTemplateProperty =
			DependencyProperty.Register("UploadFileDataTemplate", typeof(string), typeof(UploadControl), new PropertyMetadata("/HSS.Interlink.UI;component/uploadfile.xaml"));
		#endregion

		#region UploadFileType
		/// <summary>
		/// Gets or sets the fully qualified name of the Type of the model that contains an individual file for upload and must be or derive from HSS.Interlink.UI.UploadFile.
		/// </summary>
		/// <remarks>
		/// <para>
		/// This property is used to resolve the desired model which
		/// can be the default <see cref="UploadFile"/>, or a derived
		/// type allowing a control developer to extend the 
		/// <see cref="UploadFile"/> model.
		/// </para>
		/// <para>
		/// The default is 'HSS.Interlink.UI.UploadFile'.
		/// </para>
		/// </remarks>
		[DefaultValue("HSS.Interlink.UI.UploadFile")]
		[Category("Interlink")]
		[Description("The fully qualified name of the Type of the model that contains an individual file for upload and must be or inherit from HSS.Interlink.UI.UploadFile")]
		public string UploadFileType
		{
			get { return (string)GetValue(UploadFileTypeProperty); }
			set { SetValue(UploadFileTypeProperty, value); }
		}
		/// <summary>
		/// Using a DependencyProperty as the backing store for UploadFileType.  This enables animation, styling, binding, etc...
		/// </summary>
		public static readonly DependencyProperty UploadFileTypeProperty =
			DependencyProperty.Register("UploadFileType", typeof(string), typeof(UploadControl), new PropertyMetadata("HSS.Interlink.UI.UploadFile"));
		#endregion

		#endregion

		#region Misc

		/// <summary>
		/// Gets the state of the overall upload operation for all files.
		/// </summary>
		[Browsable(false)]
		public UploadState State
		{
			get { return this.viewModel.State; }
		}

		#region Files
		/// <summary>
		/// Gets a ReadOnlyCollection of the files uploading/uploaded.
		/// </summary>
		[Browsable(false)]
		public ReadOnlyCollection<UploadFile> Files
		{
			get { return new ReadOnlyCollection<UploadFile>(this.viewModel.FileList); }
		}
		/// <summary>
		/// Gets or sets the file (IUploadFile) currently being uploaded.
		/// </summary>
		[Browsable(false)]
		public UploadFile CurrentFile
		{
			get { return this.PART_FileListBox.SelectedItem as UploadFile; }
			set { this.PART_FileListBox.SelectedItem = value; }
		}
		#endregion

		#region IsBusy
		/// <summary>
		/// Gets if an upload is currently in process.
		/// </summary>
		public bool IsBusy
		{
			get
			{
				return this.viewModel.State == UploadState.InProgress;
			}
		}
		#endregion

		#endregion

		#endregion

		#region Overrides
		/// <summary>
		/// Custom implementation for the UploadControl template.
		/// </summary>
		/// <remarks>
		/// <para>
		/// It is here, where the view model (<see cref="UploadControlViewModel"/>) is created.
		/// </para>
		/// </remarks>
		public override void OnApplyTemplate()
		{
			this.UnsubscribeToTemplateEvents();

			base.OnApplyTemplate();

			this.PART_ContentRoot = this.GetTemplateChild("PART_ContentRoot") as Grid;
			this.PART_FileListBox = this.GetTemplateChild("PART_FileListBox") as ListBox;
			this.PART_FileColumnHeader = this.GetTemplateChild("PART_FileColumnHeader") as FrameworkElement;

			this.FormatTemplate();

			this.SubscribeToTemplateEvents();

		}
		#endregion

		#region Methods

		#region Public
		/// <summary>
		/// Attempts to browse for files.
		/// </summary>
		/// <remarks>
		/// <para>
		/// If there an upload is already in process, this method performs no action.
		/// </para>
		/// </remarks>
		public void Browse()
		{
			this.viewModel.Browse();
		}
		/// <summary>
		/// Attempt to begin the upload process.
		/// </summary>
		public void UploadAsync()
		{
			this.viewModel.UploadAsync();
		}
		/// <summary>
		/// Attempts to cancel the currently executing upload process.
		/// </summary>
		/// <remarks>
		/// <para>
		/// If there is no upload in process, this method performs no action.
		/// </para>
		/// </remarks>
		public void CancelAsync()
		{
			this.viewModel.CancelAsync();
		}
		/// <summary>
		/// Checks to see if a file exists by the provided file name.
		/// </summary>
		/// <param name="fileName">The name of the file to check.</param>
		/// <returns>True if the file is in the select list; otherwise false.</returns>
		public bool ContainsFile(string fileName)
		{
			return this.viewModel.ContainsFile(fileName);
		}
		/// <summary>
		/// Add a collection of files.
		/// </summary>
		/// <param name="files">A collection of <see cref="FileInfo"/> objects.</param>
		/// <returns>true if the files where added.</returns>
		public bool AddFiles(IEnumerable<FileInfo> files)
		{
			return this.viewModel.AddFiles(files);
		}
		/// <summary>
		/// Adds a single file.
		/// </summary>
		/// <param name="file">The <see cref="FileInfo"/> to add.</param>
		/// <returns>True if the file was added; otherwise false.</returns>
		public bool AddFile(FileInfo file)
		{
			return this.viewModel.AddFile(file);
		}
		#endregion

		#region Private
		private void FormatTemplate()
		{
			if (null != this.PART_FileListBox)
				this.PART_FileListBox.AllowDrop = true;

			var rd = new ResourceDictionary();
			rd.Source = new Uri(this.UploadFileDataTemplate, UriKind.RelativeOrAbsolute);
			Application.Current.Resources.MergedDictionaries.Add(rd);

			this.SetBinding(StateManager.StateProperty, new Binding("State"));

		}
		private void SubscribeToTemplateEvents()
		{
			this.SizeChanged += new SizeChangedEventHandler(UploadControl_SizeChanged);
			this.MouseRightButtonDown += new MouseButtonEventHandler(UploadControl_MouseRightButtonDown);

			if (null != this.PART_ContentRoot)
				this.PART_ContentRoot.MouseRightButtonDown += new MouseButtonEventHandler(Root_MouseRightButtonDown);

			if (null != this.PART_FileListBox)
				this.PART_FileListBox.Drop += new DragEventHandler(FileList_Drop);
		}
		private void UnsubscribeToTemplateEvents()
		{
			this.SizeChanged -= new SizeChangedEventHandler(UploadControl_SizeChanged);
			this.MouseRightButtonDown -= new MouseButtonEventHandler(UploadControl_MouseRightButtonDown);

			if (null != this.PART_ContentRoot)
				this.PART_ContentRoot.MouseRightButtonDown -= new MouseButtonEventHandler(Root_MouseRightButtonDown);

			if (null != this.PART_FileListBox)
				this.PART_FileListBox.Drop -= new DragEventHandler(FileList_Drop);
		}
		private void UpdateFileNameWidth()
		{
			if (null == this.PART_FileColumnHeader)
				return;
			if (null == this.viewModel)
				return;
			var newWidth = this.PART_FileColumnHeader.ActualWidth;
			foreach (var file in this.viewModel.Files)
				file.UpdateFileNameWidth(newWidth);
		}
		#endregion

		#endregion

		#region Event Handlers
		void UploadControl_MouseRightButtonDown(object sender, MouseButtonEventArgs e)
		{
			e.Handled = true;
		}
		void UploadControl_SizeChanged(object sender, SizeChangedEventArgs e)
		{
			this.UpdateFileNameWidth();
		}
		void Root_MouseRightButtonDown(object sender, MouseButtonEventArgs e)
		{
			e.Handled = true;
		}
		void FileList_Drop(object sender, DragEventArgs e)
		{
			if (null == e.Data)
				return;
			if (e.Data.GetDataPresent(DataFormats.FileDrop))
			{
				int current = this.PART_FileListBox.Items.Count;
				if (!this.viewModel.AllowMultiSelect && current > 0)
					return;
				var droppedFiles = e.Data.GetData(DataFormats.FileDrop) as FileInfo[];
				if (null != droppedFiles && droppedFiles.Length > 0)
					this.viewModel.AddFiles(droppedFiles);
				if (current == 0 && this.PART_FileListBox.Items.Count > 0)
					this.PART_FileListBox.SelectedIndex = 0;
				if (this.viewModel.AutoUpload && this.PART_FileListBox.Items.Count > 0)
					this.viewModel.UploadAsync();
			}
		}
		void FileList_GotFocus(object sender, RoutedEventArgs e)
		{
			var listBoxItem = e.OriginalSource as ListBoxItem;
			if (null != listBoxItem && !listBoxItem.IsSelected)
				listBoxItem.IsSelected = true;
		}
		void FileList_MouseRightButtonDown(object sender, MouseButtonEventArgs e)
		{
			e.Handled = true;
		}
		void viewModel_FileAdded(object sender, UploadManagerFileChangedEventArgs e)
		{
			this.UpdateFileNameWidth();
		}
		void viewModel_UploadCompleted(object sender, EventArgs e)
		{
			this.OnUploadCompleted(e);
		}
		void viewModel_ViewFileDetails(object sender, ViewFileDetailsEventArgs e)
		{
			this.OnViewFileDetails(e);
		}
		void viewModel_GetMetadata(object sender, GetMetadataEventArgs e)
		{
			this.OnGetMetadata(e);
		}
		#endregion
	}
	#endregion
}